<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Simple random stream</title>
    <style>
      .input {
        float: left;
        width: 50%;
      }

      .output {
        float: right;
        width: 50%;
      }

      hr {
        clear: both;
      }

      button {
        display: block;
      }
    </style>
</head>
<body>
<h1>Simple random stream</h1>

<button>Stop string generation</button>

<div class="input">
  <h2>Custom stream input</h2>
  <ul>

  </ul>
</div>

<div class="output">
  <h2>Reading custom stream</h2>
  <ul>

  </ul>
</div>

<hr>

<h2>Final result</h2>

<p></p>

<script>
  // Store reference to lists, paragraph and button
  const list1 = document.querySelector('.input ul');
  const list2 = document.querySelector('.output ul');
  const para = document.querySelector('p');
  const button = document.querySelector('button');

  // create empty string in which to store result
  let result = "";

  // function to generate random character string
  function randomChars() {
    let string = "";
    let choices = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()";

    for (let i = 0; i < 8; i++) {
      string += choices.charAt(Math.floor(Math.random() * choices.length));
    }
    return string;
  }

  const stream = new ReadableStream({
    start(controller) {
      interval = setInterval(() => {
        let string = randomChars();

        // Add the string to the stream
        controller.enqueue(string);

        // show it on the screen
        let listItem = document.createElement('li');
        listItem.textContent = string;
        list1.appendChild(listItem);
      }, 1000);

      button.addEventListener('click', function() {
        clearInterval(interval);
        readStream();
        controller.close();
      })
    },
    pull(controller) {
      // We don't really need a pull in this example
    },
    cancel() {
      // This is called if the reader cancels,
      // so we should stop generating strings
      clearInterval(interval);
    }
  });

  function readStream() {
    const reader = stream.getReader();
    let charsReceived = 0;

    // read() returns a promise that resolves
    // when a value has been received
    reader.read().then(function processText({ done, value }) {
      // Result objects contain two properties:
      // done  - true if the stream has already given you all its data.
      // value - some data. Always undefined when done is true.
      if (done) {
        console.log("Stream complete");
        para.textContent = result;
        return;
      }

      charsReceived += value.length;
      const chunk = value;
      let listItem = document.createElement('li');
      listItem.textContent = 'Read ' + charsReceived + ' characters so far. Current chunk = ' + chunk;
      list2.appendChild(listItem);

      result += chunk;

      // Read some more, and call this function again
      return reader.read().then(processText);
    });
  }
</script>
</body>
</html>
